<!DOCTYPE html>
<style>
  body {
    margin: 0;
  }
  .container {
    height: 250px;
    width: 250px;
    margin-top: 50px;
    display: block;
  }
  .container > div {
    height: 200px;
    width: 200px;
    overflow: scroll;
  }
  .container > div > div {
    height: 1000px;
    width: 1000px;
    background: linear-gradient(to bottom, red, white);
  }
  .container > iframe {
    height: 222px;
    width: 222px;
    display: block;
    border: none;
  }
</style>
<script>
  // Prefer non-composited scrolling because this is a test of non-composited
  // scrollers.
  if (window.internals)
    internals.settings.setPreferCompositingToLCDTextEnabled(false);
</script>
<section id='div' style="display:none;">
  <div class="to-show container">
  </div>
  <div class="to-hide container">
    <div>
      <div>This should not be visible.</div>
    </div>
  </div>
</section>

<section id="iframe" style="display:none;">
  <div class="to-show container">
  </div>
  <div class="to-hide container">
    <iframe src="resources/large-div-background-gradient.html">
      This should not be visible.
    </iframe>
  </div>
</section>

<p>Two squares should be visible and fully covered by green overlays.</p>

<div id="console"></div>

<script src="../resources/js-test.js"></script>
<script src="../resources/run-after-layout-and-paint.js"></script>
<script src="resources/non-fast-scrollable-region-testing.js"></script>
<script>
  window.jsTestIsAsync = true;
  description('This test ensures that changing visibility of a non-fast ' +
              'scrollable area correctly updates list of non-fast scrollable ' +
              'areas (See http://crbug.com/434982). Two types of non-fast ' +
              'scrollable regions are covered: div, and iframe.');

  onload = next;

  var testCases = [
    // type, bounding box of element to show, bounding box of element to hide
    ['div',    '[0, 50, 200, 200]', '[0, 350, 200, 200]'],
    ['iframe', '[0, 50, 222, 222]', '[0, 350, 222, 222]']];

  var index = 0;
  function next(){
    if (index < testCases.length) {
      var type = testCases[index][0];
      debug('running tests for non-fast scrollable ' + type);
      runTest.apply(this, testCases[index]);
    } else {
      // Next 3 statements have no functional impact just visual
      showSectionIf(function(){return true;});
      runAfterLayoutAndPaint(function(){
        drawNonFastScrollableRegionOverlays();

        finishJSTest();
      });
    }

    index++;
  }

  // Execute tests for given non-fast scrollable type.
  function runTest(type, elemToShowExpected, elemToHideExpected) {
    // Hide all sections that are not relevant to this test to ensure
    // |internals.nonFastScrollableRects| returns only the relevant non-
    // fast scrollable regions.
    showSectionIf(function(section){return section.id == type;});

    runAfterLayoutAndPaint(function() {
      shouldBeEqualToString('document.getElementById("' + type + '").style.display', 'block');

      var elemToShow = document.querySelector('section#' + type + '> .to-show'),
          elemToHide = document.querySelector('section#' + type + '> .to-hide');

      nonFastScrollableRects = internals.nonFastScrollableRects(document);
      shouldBe('nonFastScrollableRects.length', '1');
      shouldBeEqualToString('rectToString(nonFastScrollableRects[0])', elemToHideExpected);

      // Hide container before making it non-fast scrollable to ensure any
      // layout occurs while it is invisible.
      elemToShow.style.visibility = 'hidden';
      appendScrollable(elemToShow, type, function() {
        // Change visibility (hidden -> visible), which should not cause any layout,
        // and verify that non-fast scrollable areas are correctly updated.
        elemToShow.style.visibility = 'visible';
        shouldBe('internals.needsLayoutCount()', '0');

        runAfterLayoutAndPaint(function() {
          nonFastScrollableRects = internals.nonFastScrollableRects(document);
          shouldBe('nonFastScrollableRects.length', '2');
          shouldBeEqualToString('rectToString(nonFastScrollableRects[0])', elemToShowExpected);
          shouldBeEqualToString('rectToString(nonFastScrollableRects[1])', elemToHideExpected);

          // Change visibility (visible -> hidden)
          elemToHide.style.visibility = 'hidden';
          shouldBe('internals.needsLayoutCount()', '0');

          runAfterLayoutAndPaint(function() {
            nonFastScrollableRects = internals.nonFastScrollableRects(document);
            shouldBe('nonFastScrollableRects.length', '1');
            shouldBeEqualToString('rectToString(nonFastScrollableRects[0])', elemToShowExpected);
            next();
          });
        });
      });
    });
  }

  // Appends to container an scrollable element of the given type.
  // Note that because this layout test runs with --run-web-tests flag
  // the appended scrollable is always non-fast even on HDPI platforms.
  function appendScrollable(container, type, nextStep) {
    var inner;
    switch (type) {
    case 'div':
      inner = '<div><div>This should be covered by a green overlay.</div></div>';
      break;
    case 'iframe':
      window.nextStep = nextStep;
      inner = '<iframe id="iframe" onload="runAfterLayoutAndPaint(window.nextStep)" src="resources/large-div-background-gradient.html"></iframe>';
      break;
    }
    container.innerHTML = inner;
    if (type == 'div')
      runAfterLayoutAndPaint(nextStep);
  }

  // Shows sections that pass condition but hides others
  function showSectionIf(condition) {
    var sections = document.querySelectorAll('section');
    for (var i = 0; i < sections.length; i++)
      sections[i].style.display = condition(sections[i]) ? 'block' : 'none';
  }
</script>
